package client
{
	import flare.vis.data.Data;
	import flare.vis.data.DataSprite;
	import flare.vis.data.NodeSprite;
	import flare.vis.operator.layout.Layout;
	
	import flash.geom.Rectangle;
	import flash.utils.Dictionary;
	
	import mx.charts.renderers.DiamondItemRenderer;
	import mx.collections.ArrayCollection;
	import mx.collections.Sort;
	
	/**
	 * Layout that places nodes randomly within the layout bounds.
	 */
	public class CustomChronologicalLayout extends Layout
	{
		/** The data group to layout. */
		public var group:String;
		
		private var _numCoursesInTerm:Dictionary;
		private var _verticalIndex:Dictionary;
		private var _indexForTerm:Dictionary;
		private var _numTerms:Number;
		
		/**
		 * Creates a new CustomChronologicalLayout instance. 
		 * @param group the data group to layout
		 */
		public function CustomChronologicalLayout(group:String=Data.NODES) {
			this.group = group;
		}
		
		private function generateHorizontalMapping():void {
			// reset globals
			_indexForTerm = new Dictionary();
			_numTerms = 0;
			
			var termsDictionary:Dictionary = new Dictionary();
			var termsArray:ArrayCollection = new ArrayCollection();
			
			visualization.data.visit(function(d:DataSprite):void {
				if (d.visible) {
					termsDictionary[d.data.term] = {
						term: d.data.term,
						year: d.data.year,
						quarter: d.data.quarter
					};
				}
			}, group);
			
			for each (var t:Object in termsDictionary) {
				termsArray.addItem(t);
			}
			
			// sort the termsArray chronologically
			var sorter:Sort = new Sort();
			var quarterMap:Object = {"Aut":0, "Wtr":1, "Spr":2, "Sum":3};
			sorter.compareFunction = function(a:Object, b:Object, fields:Array = null):int {
				if ( a.year < b.year ) {
					// a comes before b
					return -1;
				} else if (b.year < a.year) {
					return 1;
				} else {
					if (quarterMap[a.quarter] < quarterMap[b.quarter]) {
						return -1;
					} else if (quarterMap[b.quarter] < quarterMap[a.quarter]) {
						return 1;
					} else {
						return 0;
					}
				}
			}
			termsArray.sort = sorter;
			termsArray.refresh();
			
			// generate a map from term to index
			_numTerms = termsArray.length;
			for (var i:uint=0; i<termsArray.length; i++) {
				_indexForTerm[termsArray[i].term] = i+1;
			}
		}
		
		private function generateVerticalMapping():void {
			// reset globals
			_verticalIndex = new Dictionary();
			_numCoursesInTerm = new Dictionary();
			
			
			var binsByTerm:Dictionary = new Dictionary();
			var maxForID:Dictionary = new Dictionary();
			
			// bin data by term
			visualization.data.visit(function(d:DataSprite):void {
				if (d.visible) {
					if (!binsByTerm[d.data.term]) {
						binsByTerm[d.data.term] = new ArrayCollection();
					};
					binsByTerm[d.data.term].addItem(d);
				}
			}, group);
			
			// sort each bin by max edge weight & generate map from node id to vertical index
			for each (var bin:ArrayCollection in binsByTerm) {
				var sorter:Sort = new Sort();
				sorter.compareFunction = function(a:Object, b:Object, fields:Array = null):int {
					
					// calculate max edge weight for each object
					var i:uint;
					var max:uint = 0;
					var tmp:uint = 0;
					
					if (!maxForID[a.data.id]) {
						for (i=0; i<NodeSprite(a).inDegree; i++) {
							tmp = NodeSprite(a).getInEdge(i).data.weight;
							max = tmp > max ? tmp : max;
						}
						for (i=0; i<NodeSprite(a).outDegree; i++) {
							tmp = NodeSprite(a).getOutEdge(i).data.weight;
							max = tmp > max ? tmp : max;
						}
						maxForID[a.data.id] = max;
					}
					
					if (!maxForID[b.data.id]) {
						max = 0;
						tmp = 0;
						for (i=0; i<NodeSprite(b).inDegree; i++) {
							tmp = NodeSprite(b).getInEdge(i).data.weight;
							max = tmp > max ? tmp : max;
						}
						for (i=0; i<NodeSprite(b).outDegree; i++) {
							tmp = NodeSprite(b).getOutEdge(i).data.weight;
							max = tmp > max ? tmp : max;
						}
						maxForID[b.data.id] = max;
					}
					
					// compare
					if ( maxForID[a.data.id] < maxForID[b.data.id] ) {
						// a comes before b
						return -1;
					} else if (maxForID[b.data.id] < maxForID[a.data.id]) {
						return 1;
					} else {
						return 0;
					}
				}
				bin.sort = sorter;
				bin.refresh();
				
				// store vertical index for each node
				for (var i:uint=0; i<bin.length; i++) {
					_verticalIndex[bin[i].data.id] = i+1;
				}
				_numCoursesInTerm[bin[0].data.term] = bin.length;
			}
		}
		
		/** @inheritDoc */
		protected override function layout():void
		{
			var r:Rectangle = layoutBounds;
			
			generateHorizontalMapping();
			generateVerticalMapping();
			
			visualization.data.visit(function(d:DataSprite):void
			{
				if (!d.fixed && d.visible) {
					var slew:Number = 0.2;
					var targetX:Number = r.x + r.width/(_numTerms + 1.0) * _indexForTerm[d.data.term];
					var targetY:Number = r.y + r.height - r.height/(_numCoursesInTerm[d.data.term] + 1.0) * _verticalIndex[d.data.id];
					var o:Object = _t.$(d);
					o.x = (targetX-o.x)*slew + o.x;
					o.y = (targetY-o.y)*slew + o.y;
				}
			}, group);
		}
		
	} // end of class CustomChronologicalLayout
}